import tools,vars,time,multiprocessing,os,traceback
from pptx import Presentation
from moviepy.video.tools.subtitles import SubtitlesClip
from PIL import Image
from proglog import ProgressBarLogger
from tts import tts_util
from ali import ali_util
from moviepy.editor import AudioFileClip,ImageClip,TextClip,concatenate,CompositeVideoClip,CompositeAudioClip
# from moviepy.editor import *


tts = tts_util.tts_util()
oss = ali_util.oss_util()
imm = ali_util.imm_util()

class MyBarLogger(ProgressBarLogger):
    step_progress = 3
    base_progress = 45 # 其它地方用了40%

    def __init__(self,progress):
        ProgressBarLogger.__init__(self,min_time_interval=2)
        self.progress = progress
        self.writing_video = False

    def callback(self, **changes):
        if self.writing_video:
            bars = self.state.get('bars')
            index = len(bars.values()) - 1
            if index > -1:
                bar = list(bars.values())[index]
                progress = int(bar['index'] / bar['total'] * 100)
                self.progress.update(int(self.base_progress + (3*self.step_progress) + (progress*0.45)))

        for (parameter, new_value) in changes.items():
            # print ('MyTqdmBarLogger Parameter %s is now %s' % (parameter, new_value))
            if len(new_value) > 0:
                if new_value.startswith("Moviepy - Building video"):
                    self.writing_video = False
                    self.progress.add(self.step_progress)
                elif new_value.startswith("MoviePy - Writing audio"):
                    self.progress.add(self.step_progress)
                elif new_value.startswith("Moviepy - Writing video"):
                    self.writing_video = True
                    self.progress.add(self.step_progress)
                elif new_value.startswith("Moviepy - video ready"):
                    self.progress.update(100)

# 进度安排：生成 png 及下载占10%，生成语音占30%,生成视频占60%
class ppt:
    def __init__(self,wp):
        self.slides = []
        self.progress = None
        self.percent = 0
        self.base_dir = ""

        self.progress = wp
        vars.gl_oss_key = tools.random_key()
        # vars.gl_oss_key = '202009/f90e9568-fbd5-11ea-b28a-acde48001122.pptx'
        vars.gl_oss_basename = tools.filepath_base_name(vars.gl_oss_key)

        self.prs = Presentation(vars.gl_filepath)

        page_num = 0 #页码
        for i in self.prs.slides:
            page_num += 1
            text = ""
            try:
                if hasattr(i,'notes_slide') and hasattr(i.notes_slide,"notes_text_frame"):
                    text = i.notes_slide.notes_text_frame.text

            except Exception:
                exstr = traceback.format_exc()
                print(exstr)
            finally:
                s = slide(page_num, text)
                print( " text in page %d is : %s" % (page_num,text))
                self.slides.append(s)


        self.page_count = len(self.prs.slides)
        # print("page count: %d" % self.page_count)

        self.base_dir = os.path.join(vars.temp_dir,vars.gl_oss_basename)
        # print("base_dir: %s" % self.base_dir)


        if not os.path.exists(self.base_dir):
            os.makedirs(self.base_dir)

    def do(self):
        self.progress.update(0)
        self.gen_wavs() # 生成语音文件并保存本地
        self.gen_pngs() # 生成图片缩略图并下载本地
        self.gen_srt()

        self.gen_mp4() # 生成视频文件

    def download(self,key, localpath):
        oss.download(key, localpath)

    def upload_oss(self,localpath, key):
        oss.upload(localpath, key)

    # 输入ppt 路径，返回 png 列表
    def ppt_to_pngkeys(self,key):
        res_dict = imm.covert(key)

        page_count = res_dict['page_count']
        message = res_dict['message']

        pngs = []
        if page_count > 0:
            dest_dir = tools.key_to_dir(key)
            for i in range(page_count):
                pngs.append(tools.oss_path_join(dest_dir, str(i + 1) + ".png"))
            return pngs
        else:
            raise Exception("PTI ERR： " + message)

    def gen_srt(self):

        # 生成字幕文件
        srt_content = ''

        index = 1
        time_offset = 0
        for slide in self.slides:
            for row in slide.text_wav_array:
                if len(row.text) == 0:
                    time_offset += row.wav_time_length
                else:
                    srt_content += str(index) + "\n" + tools.format_second(time_offset) + " --> "
                    time_offset += row.wav_time_length
                    srt_content += tools.format_second(time_offset) + "\n" + row.text + "\n\n"
                    index +=1

        f = open(os.path.join(self.base_dir,'default.srt'), 'w')  # 清空文件内容再写
        f.write(srt_content)  # 只能写字符串
        f.close()

    def gen_mp4(self):
        FONT_URL = tools.resource_path('font/chinese.msyh.ttf')
        mp4_path = tools.key_to_dir(vars.gl_filepath) + "_" + str(int(time.time())) + ".mp4"

        vicdo_clips = []
        audio_clips = []

        time_offset = 0
        png_width = 1920
        png_height = 1080

        for slide in self.slides:
            png_path = os.path.join(self.base_dir, slide.png_name)

            if os.path.exists(png_path):
                #resize
                try:
                    im = Image.open(png_path)

                    png_width = int(im.width)  # 图片宽度
                    png_height = int(im.height)  # 图片高度


                    if (vars.gl_width > png_width and vars.gl_height > png_height):
                        # 图片尺寸必须是偶数，否则视频播放会有问题
                        resize_even = False
                        if png_width % 2 != 0:
                            png_width = png_width-1
                            resize_even = True
                        if png_height % 2 != 0:
                            png_height = png_height-1
                            resize_even = True

                        if resize_even:
                            newimg = im.resize((png_width, png_height))  # (left, upper, right, lower)
                            newimg.save(png_path, "PNG")
                    else:
                        if vars.gl_width % 2 != 0:
                            vars.gl_width = vars.gl_width-1
                        if vars.gl_height % 2 != 0:
                            vars.gl_height = vars.gl_height-1

                        newimg = im.resize((vars.gl_width,vars.gl_height), Image.ANTIALIAS)
                        newimg.save(png_path, "PNG")

                except IOError as e:
                    raise e

                vicdo_clips.append(ImageClip(png_path).set_duration(slide.time_length))

                #添加音频和字幕 wav_path text wav_time_length
                for text_row in slide.text_wav_array:

                    #添加音频
                    if vars.gl_need_voice:
                        if not(len(text_row.wav_path) == 0 and text_row.wav_time_length > 0): #not space
                            audio_clips.append(AudioFileClip(text_row.wav_path).set_start(time_offset))

                    time_offset += text_row.wav_time_length

        self.progress.add(5)

        video = concatenate(vicdo_clips, method="compose")

        # 添加字幕
        if vars.gl_need_subtitiles:
            fontsize = int(png_width*40/1980)
            position = int(png_height - 3*fontsize)
            stroke_width = 0.5*png_width/1980

            generator = lambda txt: TextClip(txt, font=FONT_URL, fontsize=fontsize, color=vars.gl_color, stroke_color="gray",
                                             stroke_width=stroke_width).set_opacity(.8)
            srt_path = os.path.join(self.base_dir, 'default.srt')

            if os.path.getsize(srt_path) > 10:
                subtitles = SubtitlesClip(srt_path, generator)
                video = CompositeVideoClip([video, subtitles.set_pos(('center',position))])

        if vars.gl_need_voice and len(audio_clips) > 0:
            audio = CompositeAudioClip(audio_clips)
            video = video.set_audio(audio)
        # print("start write_videofile")

        logger = MyBarLogger(self.progress)
        video.write_videofile(mp4_path, fps=24,audio_codec="aac",verbose=True,write_logfile=False, threads=multiprocessing.cpu_count(),logger=logger) # audio_codec必须有，否则 mac 默认播放器没声音
        self.progress.update(100)

    # 生成图片 占10%进度 从0 - 10
    def gen_pngs(self):
        self.progress.add(1)
        self.upload_oss(vars.gl_filepath,vars.gl_oss_key)
        self.progress.add(1)
        pngkeys = self.ppt_to_pngkeys(vars.gl_oss_key)
        # pngkeys = ['202009/f90e9568-fbd5-11ea-b28a-acde48001122/1.png', '202009/f90e9568-fbd5-11ea-b28a-acde48001122/2.png']

        self.progress.add(3)

        for pngkey in pngkeys:
            localpath = tools.oss_path_join(self.base_dir,os.path.basename(pngkey))
            oss.download(pngkey,localpath)
        self.progress.add(5)

    # 生成语音文件 进度从10 - 40，共占30%进度
    def gen_wavs(self):
        total_progress = 30 # 共给分30%的进度，按 slide 平均分
        slide_progress = total_progress / self.page_count
        # print("slide_progress: %f" % slide_progress)

        for slide in self.slides:
            slide.gen_wav(self.base_dir)
            self.progress.add(slide_progress)

        # print("all wavs done!")

    def cal_wav_count(self):
        self.wav_count = 0
        for i in self.slides:
            self.wav_count += i.get_wav_count()

class slide:

    def __init__(self,page_num,text):
        self.text = text
        self.time_length = 0

        self.text_array = [] # 所有文本数组
        self.text_wav_array = []# 文本和语音数组

        # print("slide init text: %s " % self.text)
        self.page_num = page_num

        self.png_name = str(self.page_num) + ".png"

        br_arr = tools.text_to_arr_br(text)

        for line in br_arr:
            rows = tools.long_string_split(line,96)
            self.text_array.extend(rows)

    def get_wav_count(self):
        return len(self.text_array)

    # 生成文本对应的语音文件
    def gen_wav(self,destdir):
        # 开头空两秒
        row = slide_text_row("")
        row.space(2)
        self.text_wav_array.append(row)

        index=1 # 占位的索引是0，其它从1开始
        for text in self.text_array:
            row = slide_text_row(text)

            destpath = os.path.join(destdir,str(self.page_num) + "-" + str(index) + ".wav")
            row.gen_wav(destpath)

            index += 1

            self.text_wav_array.append(row)

        # 结尾空2秒
        row = slide_text_row("")
        row.space(2)
        self.text_wav_array.append(row)

        self.cal_time_length()

    # 计算这个片的时长
    def cal_time_length(self):
        for row in self.text_wav_array:
            self.time_length += row.wav_time_length


class slide_text_row:

    word_per_second = 4 #每秒读几个字

    def __init__(self,text):
        self.text = text
        self.wav_path = ""
        self.wav_time_length  = 0
        # print("slide_text_row init text: %s " % self.text)

    # 占空
    def space(self,time_length):
        self.wav_time_length = int(time_length)
        self.wav_path=""
        self.text = ""

    # 生成文本对应的语音文件
    def gen_wav(self,destpath):
        if len(self.text) < 1:
            return

        if self.text.startswith("$s:"):
            self.parse_cmd()
        else:
            if vars.gl_need_voice:
                tts.text_to_wav(self.text, vars.gl_voice_type, destpath)
                self.wav_path = destpath
                self.wav_time_length = tools.wav_length(self.wav_path)
                time.sleep(0.5)  # 并发控制，tts 限制
            else:
                # 不需要语音，按字数计算时长
                self.wav_path = ""
                self.wav_time_length = int(len(self.text)/self.word_per_second)

    def parse_cmd(self):
        space_time = int(self.text[3:])
        self.space(space_time)

    # 生成语音文件 default voice_type 101002
